<?php
namespace Yls\Core;

use Yls;
use Exception;

/**
 * Yls\Core\Curd
 * 数据库CURD操作类，操作的表必须含有单一主键字段
 * @example
 * $Curd = Yls::Curd('User'); // 实例化表模型
 * $Curd->Create(['name' => 'admin']); // 创建记录
 * echo $Curd->name, $Curd->addTime; // 读取属性
 * $Curd = Yls::Curd('~~buz.~Vip')->Read('name = "Yls"'); // 字段name为Yls的记录
 * $Curd = Yls::Curd('User')->Read(1); // 主键值为3的记录 
 * $Curd->name = 'keluo'; // 设置name字段值
 * $Curd->Update(); // 更新全部字段
 * $Curd->Update(['name' => 'keluo']); // 更新特定字段
 * $Curd->Delete(); // 删除记录
 * $list = Yls::Curd('User')->ReadAll('name LIKE "%a%"'); // 多条CURD实例
 * $list = Yls::Curd('User')->ReadAll(null, 'ORDER BY id DESC LIMIT 10'); // 其他条件
 * @author keluo <keluo@focrs.com>
 * @copyright 2017 Focrs, Co.,Ltd
 * @package Yls
 * @since 2015-02-24
 */
class Curd {
    
    private $_connect = null;
    private $_table = null;
    private $_fields = [];
    private $_pkName = null;
    private $_pkValue = null;
    private $_attributes;
    private $_updated = false;
    /**
     * 实例化单表原子操作模型
     * @param string $table 表名
     * @param string $connect 数据库连接标识
     * @throws Exception
     */
    public function __construct($table) {
        if(false === strpos($table, '.')) {
            $this->_table = $table;
        } else {
            list($this->_connect, $this->_table) = explode('.', trim($table, '~'));   
        }
        $structs = Yls::Db($this->_connect)->GetTableStruct($this->_table);
        if(is_null($structs['pk'])) throw new Exception('There is no primary key in table '.$this->_table);
        $this->_pkName = $structs['pk'];
        $this->_fields = $structs['fields'];
    }
    
    
    /**
     * 创建记录
     * @param array $data 数据，field=>value
     * @param boolean $replace 主键重复则替换
     * @return SCCurd
     */
    public function Create(array $data, $replace = false) {
        $this->_pkValue = Yls::Db($this->_connect)->From($this->_table)->Insert($data, $replace);
        $this->_updated = true;
        return $this->_pkValue ? $this : false;
    }
    
    /**
     * 更新记录
     * @param array $data 数据，field=>value
     */
    public function Update(array $data) {
        Yls::Db($this->_connect)->From($this->_table)->Where($this->_pkName.' = "'.$this->_pkValue.'"')->Update($data);
        if(isset($data[$this->_pkName])) $this->_pkValue = $data[$this->_pkName];
        $this->_updated = true;
        return $this;
    }
    
    /**
     * 读取记录
     * @param string $extra 附加条件，如：ORDER BY id DESC 
     * @param number|string $condition 为数值时表示主键值，为表达式则表示条件
     * @param const $lockType Db::LOCK_X|Db::LOCK_S 查询锁类型
     * @param boolean $returnOnlyAttributes 是否仅返回属性合集
     * @return Curd|false
     */
    public function Read($condition = null, $extra = null, $lockType = null, $returnOnlyAttributes = false) {
        if(is_numeric($condition)) {
            $record = Yls::Db($this->_connect)->From($this->_table)->Where($this->_pkName.' = "'.$condition.'"')->Read($lockType);
        } else {
            $record = Yls::Db($this->_connect)->Fetch('SELECT * FROM '.$this->_table.
            ($condition ? ' WHERE '.$condition : '').($extra ? (' '.$extra) : ''));
        }
        if(!$record) return false;
        $this->_attributes = $record;
        $this->_pkValue = $this->_attributes->{$this->_pkName};
        return $returnOnlyAttributes ? $record : $this;
    }
    
    /**
     * 返回多条记录 
     * @param string $condition
     * @param string $extra
     * @param const $lockType Db::LOCK_X|Db::LOCK_S 查询锁类型
     * @param boolean $returnOnlyAttributes 是否仅返回属性合集
     * @return array
     */
    public function ReadAll($condition = null, $extra = null, $lockType = null, $returnOnlyAttributes = false) {
        $records = Yls::Db($this->_connect)->FetchAll('SELECT * FROM '.$this->_table.
        ($condition ? ' WHERE '.$condition : '').($extra ? (' '.$extra) : ''));
        $data = [];
        foreach($records as $record) {
            $Curd = new Curd('~~'.$this->_connect.'.'.$this->_table);
            $Curd->SetAttributes($record);
            array_push($data, $returnOnlyAttributes ? $Curd->Attributes() : $Curd);
        }
        return $data;
    }
    
    /**
     * 获取所有属性
     * @return stdClass
     */
    public function Attributes() {
        if($this->_updated) {
            $this->_attributes = $this->Read($this->_pkValue, null, null, true);
            $this->_updated = false;
        }
        return $this->_attributes;
    }
    
    /**
     * 删除记录
     */
    public function Delete() {
        unset($this);
        return Yls::Db($this->_connect)->From($this->_table)->Where($this->_pkName.' = "'.$this->_pkValue.'"')->Delete();
    }
    
    /**
     * 获取字段值
     * @param string $field
     * @throws Exception
     */
    public function __get($field) {
        $attributes = $this->Attributes();
        if(in_array($field, $this->_fields)) {
            return $attributes->$field;
        } else {
            throw new Exception('There is no field named "'.$field.'" in table "'.$this->_table.'"');
        }
    }
    
    public function __toString() {
        return json_encode($this->Attributes());
    }
    
    private function SetAttributes(\stdClass $attributes) {
        $this->_attributes = $attributes;
        $this->_pkValue = $this->_attributes->{$this->_pkName};
    }
}